package com.absir.android.app;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import android.app.Application;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;

import com.absir.android.context.ContextUtils;
import com.absir.android.view.InjectViewUtils;
import com.absir.bean.basis.Configure;
import com.absir.bean.core.BeanFactoryProvider;
import com.absir.bean.core.BeanFactoryUtils;
import com.absir.bean.inject.InjectInvoker;
import com.absir.core.kernel.KernelArray;
import com.absir.core.kernel.KernelCollection;
import com.absir.core.kernel.KernelLang.BreakException;
import com.absir.core.kernel.KernelLang.CallbackBreak;
import com.absir.core.kernel.KernelLang.FilterTemplate;
import com.absir.core.kernel.KernelObject;
import com.absir.core.kernel.KernelPattern;
import com.absir.core.kernel.KernelReflect;
import com.absir.core.kernel.KernelString;

/**
 * Copyright 2013 ABSir's Studio
 * 
 * All right reserved
 *
 * Create on 2013-6-9 下午3:58:36
 */

/**
 * @author absir
 * 
 */
@Configure
public class ApplicationBean extends Application {

	/** BASE_PACKAGE */
	private static final String BASE_PACKAGE = "base-package";

	/** EXCLUDE_PACKAGE */
	private static final String EXCLUDE_PACKAGE = "exclude-package";

	/** MATCH_PATTERN */
	private static final String MATCH_PATTERN = "match-pattern";

	/** EXCLUDE_PATTERN */
	private static final String EXCLUDE_PATTERN = "exclude-pattern";

	/** Application */
	private static Application Application;

	/**
	 * @return
	 */
	public static Application getApplication() {
		return Application;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see android.app.Application#onCreate()
	 */
	@Override
	public final void onCreate() {
		Application = this;
		InjectViewUtils.setApplication(this);
		Object mainThread = KernelObject.declaredGet(getBaseContext(), "mMainThread");
		Field instrumentationField = KernelReflect.declaredField(mainThread.getClass(), "mInstrumentation");
		Object instrumentation = new InstrumentationBean();
		KernelObject.copy(KernelReflect.get(mainThread, instrumentationField), instrumentation);
		KernelReflect.set(mainThread, instrumentationField, instrumentation);

		BeanFactoryProvider provider = getBeanFactoryProvider();
		InjectInvoker[][] injectInvokers = ContextUtils.getCreateInvokers(this.getClass(), true);
		for (InjectInvoker injectInvoker : injectInvokers[0]) {
			injectInvoker.invoke(BeanFactoryUtils.getBeanFactory(), this);
		}

		super.onCreate();
		provider.started();
		for (InjectInvoker injectInvoker : injectInvokers[1]) {
			injectInvoker.invoke(BeanFactoryUtils.getBeanFactory(), this);
		}
	}

	/**
	 * @param metaData
	 * @param key
	 * @return
	 */
	private String[] getStringArray(Bundle metaData, String key) {
		if (metaData == null) {
			return null;
		}

		String value = metaData.getString(key);
		if (value == null) {
			return null;
		}

		return value.split(",");
	}

	/** ABSIR_BEAN_PACKAGE */
	private static final String ABSIR_BEAN_PACKAGE = "com.absir";

	/**
	 * @return
	 */
	private BeanFactoryProvider getBeanFactoryProvider() {
		Bundle metaData = null;
		try {
			metaData = this.getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA).metaData;

		} catch (NameNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		List<String> basePackages = metaData == null ? null : KernelArray
				.toList(getStringArray(metaData, BASE_PACKAGE));
		if (basePackages == null) {
			basePackages = new ArrayList<String>();
			basePackages.add(getPackageName());

		} else if (basePackages.size() <= 0) {
			basePackages.add(getPackageName());
		}

		basePackages.add(ABSIR_BEAN_PACKAGE);
		List<String> packageNames = new ArrayList<String>();
		for (String packageName : basePackages) {
			for (String basePackage : basePackages) {
				if (packageName != basePackage && packageName.startsWith(basePackage)) {
					packageName = null;
					break;
				}
			}

			if (packageName != null) {
				packageNames.add(packageName);
			}
		}

		final ClassLoader classLoader = ApplicationBean.class.getClassLoader();
		final String[] includePackages = KernelCollection.toArray(packageNames, String.class);
		final String[] excludePackages = getStringArray(metaData, EXCLUDE_PACKAGE);
		final Pattern[] matchPatterns = KernelPattern.getPatterns(getStringArray(metaData, MATCH_PATTERN), 0);
		final Pattern[] excludePatterns = KernelPattern.getPatterns(getStringArray(metaData, EXCLUDE_PATTERN), 0);
		final Set<Class<?>> beanTypes = new HashSet<Class<?>>();
		DalvikHelper.doScannerFilter(this, new CallbackBreak<String>() {

			@Override
			public void doWith(String template) throws BreakException {
				// TODO Auto-generated method stub
				try {
					Class<?> beanType = classLoader.loadClass(template);

					if (!(beanType.isInterface() || beanType.isPrimitive() || beanType.isAnonymousClass())) {
						beanTypes.add(beanType);
					}

				} catch (ClassNotFoundException e) {
				}
			}

		}, new FilterTemplate<String>() {

			@Override
			public boolean doWith(String template) throws BreakException {
				// TODO Auto-generated method stub
				if (template.startsWith(ABSIR_BEAN_PACKAGE)) {
					return true;
				}

				if (KernelString.matchStrings(template, excludePackages)
						|| KernelPattern.matchPatterns(template, excludePatterns)) {
					return false;
				}

				if (KernelString.matchStrings(template, includePackages)
						|| KernelPattern.matchPatterns(template, matchPatterns)) {
					return true;
				}

				return false;
			}
		});

		return new BeanFactoryProvider(beanTypes, "application", this);
	}
}
